Skip to main content

Queue

A comprehensive guide to mastering queue patterns and techniques for Data Structures and Algorithms interviews and competitive programming.

Table of Contents

  1. Introduction
  2. Basic Queue Operations
  3. Queue Implementations
  4. BFS and Level-Order Traversal
  5. Sliding Window Techniques
  6. Deque (Double-Ended Queue)
  7. Priority Queue Applications
  8. Monotonic Queue
  9. Queue-Based Algorithms
  10. Advanced Patterns
  11. Problem-Solving Framework
  12. Practice Problems

Introduction

Queues follow the FIFO (First In, First Out) principle and are fundamental for many algorithmic patterns. They're essential for:

  • Breadth-First Search (BFS) traversals
  • Level-order processing in trees and graphs
  • Sliding window problems with deque optimization
  • Task scheduling and buffering
  • Stream processing and real-time systems

When to Use Queues

Use when you need:

  • Process elements in order of arrival
  • Level-by-level traversal (BFS)
  • Sliding window with efficient add/remove from both ends
  • Task scheduling or buffering
  • Finding shortest paths in unweighted graphs

Avoid when:

  • Need random access to elements
  • LIFO behavior is required (use stack instead)
  • Need frequent middle insertions/deletions

Basic Queue Operations

Standard Queue Interface

class Queue {
constructor() {
this.items = [];
this.front = 0;
this.rear = 0;
}

// Add element to rear
enqueue(item) {
this.items[this.rear] = item;
this.rear++;
}

// Remove element from front
dequeue() {
if (this.isEmpty()) return undefined;

const item = this.items[this.front];
this.items[this.front] = undefined; // Clean up
this.front++;

// Reset when queue becomes empty
if (this.front === this.rear) {
this.front = 0;
this.rear = 0;
}

return item;
}

// Peek at front element
peek() {
if (this.isEmpty()) return undefined;
return this.items[this.front];
}

// Check if queue is empty
isEmpty() {
return this.front === this.rear;
}

// Get queue size
size() {
return this.rear - this.front;
}

// Clear the queue
clear() {
this.items = [];
this.front = 0;
this.rear = 0;
}
}

Time Complexities:

  • Enqueue: O(1)
  • Dequeue: O(1)
  • Peek: O(1)
  • Size: O(1)

Queue Implementations

1. Array-Based Queue (Circular Buffer)

Efficient implementation avoiding array shifting:

class CircularQueue {
constructor(capacity = 10) {
this.items = new Array(capacity);
this.front = 0;
this.rear = 0;
this.size = 0;
this.capacity = capacity;
}

enqueue(item) {
if (this.isFull()) {
throw new Error("Queue is full");
}

this.items[this.rear] = item;
this.rear = (this.rear + 1) % this.capacity;
this.size++;
}

dequeue() {
if (this.isEmpty()) return undefined;

const item = this.items[this.front];
this.items[this.front] = undefined;
this.front = (this.front + 1) % this.capacity;
this.size--;

return item;
}

isFull() {
return this.size === this.capacity;
}

isEmpty() {
return this.size === 0;
}

peek() {
return this.isEmpty() ? undefined : this.items[this.front];
}
}

2. Linked List-Based Queue

Dynamic size with efficient operations:

class QueueNode {
constructor(val) {
this.val = val;
this.next = null;
}
}

class LinkedQueue {
constructor() {
this.front = null;
this.rear = null;
this.size = 0;
}

enqueue(val) {
const newNode = new QueueNode(val);

if (this.rear) {
this.rear.next = newNode;
} else {
this.front = newNode; // First element
}

this.rear = newNode;
this.size++;
}

dequeue() {
if (!this.front) return undefined;

const val = this.front.val;
this.front = this.front.next;

if (!this.front) {
this.rear = null; // Queue became empty
}

this.size--;
return val;
}

peek() {
return this.front ? this.front.val : undefined;
}

isEmpty() {
return this.front === null;
}
}

3. Two-Stack Queue Implementation

Queue using two stacks (interview favorite):

class TwoStackQueue {
constructor() {
this.inStack = []; // For enqueue operations
this.outStack = []; // For dequeue operations
}

enqueue(item) {
this.inStack.push(item);
}

dequeue() {
this._moveElements();
return this.outStack.pop();
}

peek() {
this._moveElements();
return this.outStack[this.outStack.length - 1];
}

isEmpty() {
return this.inStack.length === 0 && this.outStack.length === 0;
}

size() {
return this.inStack.length + this.outStack.length;
}

// Move elements from inStack to outStack when needed
_moveElements() {
if (this.outStack.length === 0) {
while (this.inStack.length > 0) {
this.outStack.push(this.inStack.pop());
}
}
}
}

Amortized Analysis: Each element is moved at most twice, so average O(1) per operation.


BFS and Level-Order Traversal

1. Binary Tree Level-Order Traversal

Problem: Traverse tree level by level, returning each level as a separate array.

function levelOrder(root) {
if (!root) return [];

const result = [];
const queue = [root];

while (queue.length > 0) {
const levelSize = queue.length;
const currentLevel = [];

// Process all nodes at current level
for (let i = 0; i < levelSize; i++) {
const node = queue.shift();
currentLevel.push(node.val);

// Add children to queue for next level
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}

result.push(currentLevel);
}

return result;
}

Time Complexity: O(n) | Space Complexity: O(w) where w is maximum width

2. Zigzag Level Order Traversal

Problem: Traverse tree in zigzag pattern (left-to-right, then right-to-left alternately).

function zigzagLevelOrder(root) {
if (!root) return [];

const result = [];
const queue = [root];
let leftToRight = true;

while (queue.length > 0) {
const levelSize = queue.length;
const currentLevel = [];

for (let i = 0; i < levelSize; i++) {
const node = queue.shift();

// Add to front or back based on direction
if (leftToRight) {
currentLevel.push(node.val);
} else {
currentLevel.unshift(node.val);
}

if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}

result.push(currentLevel);
leftToRight = !leftToRight; // Flip direction
}

return result;
}

3. Binary Tree Right Side View

Problem: Return values of nodes you can see from the right side of tree.

function rightSideView(root) {
if (!root) return [];

const result = [];
const queue = [root];

while (queue.length > 0) {
const levelSize = queue.length;

for (let i = 0; i < levelSize; i++) {
const node = queue.shift();

// Last node in level is visible from right
if (i === levelSize - 1) {
result.push(node.val);
}

if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}

return result;
}

4. Graph BFS (Shortest Path)

Problem: Find shortest path in unweighted graph.

function shortestPath(graph, start, target) {
if (start === target) return 0;

const queue = [[start, 0]]; // [node, distance]
const visited = new Set([start]);

while (queue.length > 0) {
const [node, distance] = queue.shift();

// Check all neighbors
for (const neighbor of graph[node] || []) {
if (neighbor === target) {
return distance + 1;
}

if (!visited.has(neighbor)) {
visited.add(neighbor);
queue.push([neighbor, distance + 1]);
}
}
}

return -1; // Path not found
}

5. Word Ladder (BFS Application)

Problem: Find minimum transformation sequence from start word to end word.

function ladderLength(beginWord, endWord, wordList) {
const wordSet = new Set(wordList);
if (!wordSet.has(endWord)) return 0;

const queue = [[beginWord, 1]]; // [word, transformations]
const visited = new Set([beginWord]);

while (queue.length > 0) {
const [word, steps] = queue.shift();

if (word === endWord) {
return steps;
}

// Try all possible one-letter transformations
for (let i = 0; i < word.length; i++) {
for (let c = 97; c <= 122; c++) { // 'a' to 'z'
const newChar = String.fromCharCode(c);
if (newChar === word[i]) continue;

const newWord = word.slice(0, i) + newChar + word.slice(i + 1);

if (wordSet.has(newWord) && !visited.has(newWord)) {
visited.add(newWord);
queue.push([newWord, steps + 1]);
}
}
}
}

return 0; // No transformation possible
}

Sliding Window Techniques

1. Sliding Window Maximum (Deque)

Problem: Find maximum in each sliding window of size k.

function maxSlidingWindow(nums, k) {
const result = [];
const deque = []; // Stores indices

for (let i = 0; i < nums.length; i++) {
// Remove indices outside current window
while (deque.length > 0 && deque[0] <= i - k) {
deque.shift();
}

// Remove indices with smaller values (maintain decreasing order)
while (deque.length > 0 && nums[deque[deque.length - 1]] <= nums[i]) {
deque.pop();
}

deque.push(i);

// Add maximum to result when window is complete
if (i >= k - 1) {
result.push(nums[deque[0]]);
}
}

return result;
}

Time Complexity: O(n) | Space Complexity: O(k)

💡 Key Insight: Deque maintains indices in decreasing order of their values, so front always has the maximum.

2. First Negative in Window

Problem: Find first negative number in each sliding window.

function firstNegativeInWindow(arr, k) {
const result = [];
const queue = []; // Stores indices of negative numbers

for (let i = 0; i < arr.length; i++) {
// Remove indices outside current window
while (queue.length > 0 && queue[0] <= i - k) {
queue.shift();
}

// Add current index if number is negative
if (arr[i] < 0) {
queue.push(i);
}

// Add result when window is complete
if (i >= k - 1) {
if (queue.length > 0) {
result.push(arr[queue[0]]);
} else {
result.push(0); // No negative number in window
}
}
}

return result;
}

3. Generate Binary Numbers

Problem: Generate binary representations of numbers 1 to n using queue.

function generateBinary(n) {
if (n <= 0) return [];

const result = [];
const queue = ["1"];

for (let i = 0; i < n; i++) {
const current = queue.shift();
result.push(current);

// Generate next binary numbers by appending 0 and 1
queue.push(current + "0");
queue.push(current + "1");
}

return result;
}

Pattern Recognition: Each binary number generates two new numbers by appending 0 and 1.


Deque (Double-Ended Queue)

JavaScript Deque Implementation

class Deque {
constructor() {
this.items = [];
this.front = 0;
this.rear = 0;
}

// Add to front
addFront(item) {
this.front--;
this.items[this.front] = item;
}

// Add to rear
addRear(item) {
this.items[this.rear] = item;
this.rear++;
}

// Remove from front
removeFront() {
if (this.isEmpty()) return undefined;

const item = this.items[this.front];
this.items[this.front] = undefined;
this.front++;

if (this.front === this.rear) {
this.front = 0;
this.rear = 0;
}

return item;
}

// Remove from rear
removeRear() {
if (this.isEmpty()) return undefined;

this.rear--;
const item = this.items[this.rear];
this.items[this.rear] = undefined;

if (this.front === this.rear) {
this.front = 0;
this.rear = 0;
}

return item;
}

peekFront() {
return this.isEmpty() ? undefined : this.items[this.front];
}

peekRear() {
return this.isEmpty() ? undefined : this.items[this.rear - 1];
}

isEmpty() {
return this.front === this.rear;
}

size() {
return this.rear - this.front;
}
}

Palindrome Checker with Deque

function isPalindrome(str) {
const deque = new Deque();
const cleanStr = str.toLowerCase().replace(/[^a-z0-9]/g, '');

// Add all characters to deque
for (const char of cleanStr) {
deque.addRear(char);
}

// Compare characters from both ends
while (deque.size() > 1) {
if (deque.removeFront() !== deque.removeRear()) {
return false;
}
}

return true;
}

Priority Queue Applications

1. Priority Queue with Heap

class PriorityQueue {
constructor(compareFunc = (a, b) => a - b) {
this.heap = [];
this.compare = compareFunc;
}

enqueue(item) {
this.heap.push(item);
this._bubbleUp(this.heap.length - 1);
}

dequeue() {
if (this.isEmpty()) return undefined;

const root = this.heap[0];
const last = this.heap.pop();

if (!this.isEmpty()) {
this.heap[0] = last;
this._bubbleDown(0);
}

return root;
}

peek() {
return this.isEmpty() ? undefined : this.heap[0];
}

isEmpty() {
return this.heap.length === 0;
}

size() {
return this.heap.length;
}

_bubbleUp(index) {
while (index > 0) {
const parentIndex = Math.floor((index - 1) / 2);

if (this.compare(this.heap[index], this.heap[parentIndex]) >= 0) {
break;
}

[this.heap[index], this.heap[parentIndex]] =
[this.heap[parentIndex], this.heap[index]];

index = parentIndex;
}
}

_bubbleDown(index) {
while (true) {
let minIndex = index;
const leftChild = 2 * index + 1;
const rightChild = 2 * index + 2;

if (leftChild < this.heap.length &&
this.compare(this.heap[leftChild], this.heap[minIndex]) < 0) {
minIndex = leftChild;
}

if (rightChild < this.heap.length &&
this.compare(this.heap[rightChild], this.heap[minIndex]) < 0) {
minIndex = rightChild;
}

if (minIndex === index) break;

[this.heap[index], this.heap[minIndex]] =
[this.heap[minIndex], this.heap[index]];

index = minIndex;
}
}
}

2. Task Scheduler

Problem: Schedule tasks with cooldown period to minimize idle time.

function leastInterval(tasks, n) {
// Count frequency of each task
const taskCount = new Map();
for (const task of tasks) {
taskCount.set(task, (taskCount.get(task) || 0) + 1);
}

// Max heap based on frequency
const pq = new PriorityQueue((a, b) => b - a);
for (const count of taskCount.values()) {
pq.enqueue(count);
}

let time = 0;
const queue = []; // [count, availableTime]

while (!pq.isEmpty() || queue.length > 0) {
time++;

// Move tasks back to priority queue when cooldown is over
if (queue.length > 0 && queue[0][1] === time) {
const count = queue.shift()[0];
pq.enqueue(count);
}

// Execute highest frequency task
if (!pq.isEmpty()) {
const count = pq.dequeue() - 1;
if (count > 0) {
queue.push([count, time + n + 1]);
}
}
}

return time;
}

3. Top K Frequent Elements

Problem: Find k most frequent elements in array.

function topKFrequent(nums, k) {
// Count frequencies
const freqMap = new Map();
for (const num of nums) {
freqMap.set(num, (freqMap.get(num) || 0) + 1);
}

// Min heap of size k
const pq = new PriorityQueue((a, b) => a[1] - b[1]);

for (const [num, freq] of freqMap.entries()) {
pq.enqueue([num, freq]);

if (pq.size() > k) {
pq.dequeue();
}
}

const result = [];
while (!pq.isEmpty()) {
result.push(pq.dequeue()[0]);
}

return result.reverse();
}

Monotonic Queue

A queue that maintains elements in monotonic (increasing/decreasing) order.

1. Largest Rectangle in Histogram

Problem: Find area of largest rectangle in histogram.

function largestRectangleArea(heights) {
const stack = []; // Monotonic increasing stack (acts like queue)
let maxArea = 0;

for (let i = 0; i <= heights.length; i++) {
const currentHeight = i === heights.length ? 0 : heights[i];

while (stack.length > 0 && heights[stack[stack.length - 1]] > currentHeight) {
const height = heights[stack.pop()];
const width = stack.length === 0 ? i : i - stack[stack.length - 1] - 1;
maxArea = Math.max(maxArea, height * width);
}

stack.push(i);
}

return maxArea;
}

2. Sliding Window Minimum

Problem: Find minimum in each sliding window using monotonic deque.

function slidingWindowMinimum(nums, k) {
const result = [];
const deque = []; // Stores indices in increasing order of values

for (let i = 0; i < nums.length; i++) {
// Remove indices outside window
while (deque.length > 0 && deque[0] <= i - k) {
deque.shift();
}

// Maintain increasing order
while (deque.length > 0 && nums[deque[deque.length - 1]] >= nums[i]) {
deque.pop();
}

deque.push(i);

if (i >= k - 1) {
result.push(nums[deque[0]]);
}
}

return result;
}

Queue-Based Algorithms

1. Breadth-First Search Template

function bfsTemplate(start, isTarget, getNeighbors) {
const queue = [start];
const visited = new Set([start]);
const parent = new Map();
let level = 0;

while (queue.length > 0) {
const levelSize = queue.length;

// Process all nodes at current level
for (let i = 0; i < levelSize; i++) {
const node = queue.shift();

if (isTarget(node)) {
return reconstructPath(node, parent);
}

for (const neighbor of getNeighbors(node)) {
if (!visited.has(neighbor)) {
visited.add(neighbor);
parent.set(neighbor, node);
queue.push(neighbor);
}
}
}

level++;
}

return null; // Target not found
}

function reconstructPath(target, parent) {
const path = [];
let current = target;

while (current !== undefined) {
path.unshift(current);
current = parent.get(current);
}

return path;
}

2. Multi-Source BFS

Problem: Find distance from nearest obstacle/source for all cells.

function nearestObstacle(matrix) {
const rows = matrix.length;
const cols = matrix[0].length;
const queue = [];
const distances = Array(rows).fill().map(() => Array(cols).fill(Infinity));

// Add all obstacles to queue as starting points
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (matrix[i][j] === 1) { // Obstacle
queue.push([i, j, 0]);
distances[i][j] = 0;
}
}
}

const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];

while (queue.length > 0) {
const [row, col, dist] = queue.shift();

for (const [dr, dc] of directions) {
const newRow = row + dr;
const newCol = col + dc;

if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
const newDist = dist + 1;

if (newDist < distances[newRow][newCol]) {
distances[newRow][newCol] = newDist;
queue.push([newRow, newCol, newDist]);
}
}
}
}

return distances;
}

3. Rotting Oranges

Problem: Find minimum time for all oranges to rot.

function orangesRotting(grid) {
const rows = grid.length;
const cols = grid[0].length;
const queue = [];
let freshCount = 0;

// Find initial rotten oranges and count fresh ones
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (grid[i][j] === 2) {
queue.push([i, j, 0]); // [row, col, time]
} else if (grid[i][j] === 1) {
freshCount++;
}
}
}

if (freshCount === 0) return 0;

const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
let maxTime = 0;

while (queue.length > 0) {
const [row, col, time] = queue.shift();

for (const [dr, dc] of directions) {
const newRow = row + dr;
const newCol = col + dc;

if (newRow >= 0 && newRow < rows &&
newCol >= 0 && newCol < cols &&
grid[newRow][newCol] === 1) {

grid[newRow][newCol] = 2; // Rot the orange
freshCount--;
maxTime = Math.max(maxTime, time + 1);
queue.push([newRow, newCol, time + 1]);
}
}
}

return freshCount === 0 ? maxTime : -1;
}

Advanced Patterns

1. Queue Reconstruction by Height

Problem: Reconstruct queue based on height and people in front.

function reconstructQueue(people) {
// Sort by height desc, then by k asc
people.sort((a, b) => {
if (a[0] !== b[0]) {
return b[0] - a[0]; // Taller first
}
return a[1] - b[1]; // Smaller k first
});

const result = [];

// Insert each person at position k
for (const person of people) {
result.splice(person[1], 0, person);
}

return result;
}

2. Design Hit Counter

Problem: Count hits in the last 5 minutes using queue.

class HitCounter {
constructor() {
this.hits = []; // Queue of timestamps
}

hit(timestamp) {
this.hits.push(timestamp);
this._removeOldHits(timestamp);
}

getHits(timestamp) {
this._removeOldHits(timestamp);
return this.hits.length;
}

_removeOldHits(timestamp) {
// Remove hits older than 5 minutes (300 seconds)
while (this.hits.length > 0 &&
this.hits[0] <= timestamp - 300) {
this.hits.shift();
}
}
}

3. Shortest Bridge (BFS + DFS)

Problem: Find shortest bridge between two islands.

function shortestBridge(grid) {
const rows = grid.length;
const cols = grid[0].length;
const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];

// Find first island using DFS
const firstIsland = [];
let found = false;

for (let i = 0; i < rows && !found; i++) {
for (let j = 0; j < cols && !found; j++) {
if (grid[i][j] === 1) {
dfs(i, j);
found = true;
}
}
}

function dfs(row, col) {
if (row < 0 || row >= rows || col < 0 || col >= cols ||
grid[row][col] !== 1) {
return;
}

grid[row][col] = 2; // Mark as visited
firstIsland.push([row, col]);

for (const [dr, dc] of directions) {
dfs(row + dr, col + dc);
}
}

// BFS from first island to find second island
const queue = [...firstIsland.map(pos => [...pos, 0])];

while (queue.length > 0) {
const [row, col, distance] = queue.shift();

for (const [dr, dc] of directions) {
const newRow = row + dr;
const newCol = col + dc;

if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
if (grid[newRow][newCol] === 1) {
return distance; // Found second island
} else if (grid[newRow][newCol] === 0) {
grid[newRow][newCol] = 2; // Mark as visited
queue.push([newRow, newCol, distance + 1]);
}
}
}
}

return -1; // Should never reach here
}

4. Design Circular Queue

Problem: Implement a circular queue with fixed size.

class MyCircularQueue {
constructor(k) {
this.size = k;
this.queue = new Array(k);
this.front = 0;
this.rear = 0;
this.count = 0;
}

enQueue(value) {
if (this.isFull()) return false;

this.queue[this.rear] = value;
this.rear = (this.rear + 1) % this.size;
this.count++;
return true;
}

deQueue() {
if (this.isEmpty()) return false;

this.queue[this.front] = undefined;
this.front = (this.front + 1) % this.size;
this.count--;
return true;
}

Front() {
return this.isEmpty() ? -1 : this.queue[this.front];
}

Rear() {
if (this.isEmpty()) return -1;
const rearIndex = (this.rear - 1 + this.size) % this.size;
return this.queue[rearIndex];
}

isEmpty() {
return this.count === 0;
}

isFull() {
return this.count === this.size;
}
}

5. Moving Average from Data Stream

Problem: Calculate moving average of last size numbers.

class MovingAverage {
constructor(size) {
this.size = size;
this.queue = [];
this.sum = 0;
}

next(val) {
this.queue.push(val);
this.sum += val;

// Remove oldest element if queue exceeds size
if (this.queue.length > this.size) {
this.sum -= this.queue.shift();
}

return this.sum / this.queue.length;
}
}

Problem-Solving Framework

Queue Pattern Recognition

  1. Level-by-level processing → BFS with queue
  2. Shortest path in unweighted graph → BFS
  3. Sliding window maximum/minimum → Deque
  4. First/last element in window → Queue with cleanup
  5. Multi-source shortest path → Multi-source BFS
  6. Tree/graph traversal by levels → Level-order BFS

Step-by-Step Approach

function queueProblemTemplate(input) {
// 1. Identify the pattern
// - BFS traversal?
// - Sliding window?
// - Level processing?

// 2. Choose appropriate queue type
const queue = []; // or Deque, PriorityQueue

// 3. Initialize with starting state
queue.push(initialState);
const visited = new Set(); // if needed

// 4. Process until queue is empty
while (queue.length > 0) {
// Level processing (optional)
const levelSize = queue.length;

for (let i = 0; i < levelSize; i++) {
const current = queue.shift();

// Process current element
if (isTarget(current)) {
return result;
}

// Add neighbors/next states
for (const next of getNext(current)) {
if (!visited.has(next)) {
visited.add(next);
queue.push(next);
}
}
}
}

return defaultResult;
}

Practice Problems

Beginner Level

  1. Implement Queue using Stacks
  2. Binary Tree Level Order Traversal
  3. First Negative Number in Window
  4. Generate Binary Numbers
  5. Design Circular Queue

Intermediate Level

  1. Sliding Window Maximum
  2. Rotting Oranges
  3. Word Ladder
  4. Binary Tree Zigzag Traversal
  5. Task Scheduler
  6. Moving Average from Data Stream

Advanced Level

  1. Shortest Bridge
  2. Queue Reconstruction by Height
  3. Design Hit Counter
  4. Largest Rectangle in Histogram
  5. Minimum Window Substring
  6. Cut Off Trees for Golf Event

Expert Level

  1. Alien Dictionary (Topological Sort + Queue)
  2. Bus Routes (BFS with queue)
  3. Sliding Window Median (Two heaps + deque)
  4. Jump Game IV (BFS optimization)
  5. Race Car (BFS state space)

Time Complexity Summary

Operation/AlgorithmTime ComplexitySpace ComplexityUse Case
Queue OperationsO(1)O(1)Basic enqueue/dequeue
BFS TraversalO(V + E)O(V)Graph/tree traversal
Level OrderO(n)O(w)Tree level processing
Sliding Window MaxO(n)O(k)Window optimization
Priority Queue OpsO(log n)O(n)Heap-based operations
Multi-source BFSO(V + E)O(V)Multiple starting points

Common Patterns Summary

1. BFS Template

const queue = [start];
const visited = new Set([start]);

while (queue.length > 0) {
const node = queue.shift();
// Process node
for (const neighbor of getNeighbors(node)) {
if (!visited.has(neighbor)) {
visited.add(neighbor);
queue.push(neighbor);
}
}
}

2. Level Processing Template

while (queue.length > 0) {
const levelSize = queue.length;
const currentLevel = [];

for (let i = 0; i < levelSize; i++) {
const node = queue.shift();
currentLevel.push(node.val);
// Add children
}

result.push(currentLevel);
}

3. Sliding Window with Deque

const deque = [];
for (let i = 0; i < arr.length; i++) {
// Remove outside window
while (deque.length > 0 && deque[0] <= i - k) {
deque.shift();
}

// Maintain monotonic property
while (deque.length > 0 && condition) {
deque.pop();
}

deque.push(i);
if (i >= k - 1) result.push(arr[deque[0]]);
}

4. Multi-source BFS

const queue = [];
// Add all sources
for (const source of sources) {
queue.push([source, 0]);
visited.add(source);
}

while (queue.length > 0) {
const [node, distance] = queue.shift();
// Process and add neighbors
}

Key Takeaways

Advantages of Queues

  • FIFO ordering ensures proper sequence
  • Level-by-level processing for trees/graphs
  • Optimal for BFS and shortest path problems
  • Sliding window optimization with deque
  • Efficient buffering and task scheduling

⚠️ Common Pitfalls

  • Forgetting to check empty queue before dequeue
  • Not handling edge cases (empty input, single element)
  • Incorrect level processing in BFS
  • Memory leaks from not cleaning up references
  • Wrong queue type for the problem

🎯 Best Practices

  • Use appropriate queue implementation for the problem
  • Validate input and handle edge cases
  • Choose right data structure: Array, LinkedList, or specialized queue
  • Consider space optimization for large datasets
  • Test with various inputs including edge cases

🧠 Memory Tricks

  • "Queue = Line" → First come, first served
  • BFS = Queue → Level by level exploration
  • Deque = Double power → Add/remove from both ends
  • Priority Queue = VIP line → Most important first
  • Sliding window = Moving frame → Deque for optimization

Quick Reference Cheat Sheet

// Basic Queue Operations
queue.push(item); // enqueue
queue.shift(); // dequeue
queue[0]; // peek front

// BFS Pattern
while (queue.length > 0) {
const node = queue.shift();
// process node
// add neighbors to queue
}

// Level Order Pattern
while (queue.length > 0) {
const size = queue.length;
for (let i = 0; i < size; i++) {
const node = queue.shift();
// process level
}
}

// Sliding Window Maximum (Deque)
while (deque[0] <= i - k) deque.shift();
while (nums[deque[deque.length-1]] <= nums[i]) deque.pop();
deque.push(i);

// Priority Queue (Min Heap)
const pq = new PriorityQueue((a, b) => a - b);
pq.enqueue(item);
pq.dequeue(); // returns minimum